/******************************************************************************
** 公司名称：天津柯迪斯科技有限公司
** 版权信息：
** 文件名称： App_Protocol_BF_UART.c
** 模块名称： 八方串口协议处理模块
** 版 本 号： V1.0
** 作    者： 蒲磊
**
** 修改记录： 版本     修改人      时间         修改内容
**           V1.1    蒲磊      2022.5.23
**           V1.2    蒲磊      2023.2.16     1.增加了校准应答，且应答函数中增加了40ms的延时（为了适配串口工装）
******************************************************************************/

/******************************************************************************
* 头文件
******************************************************************************/
#include <string.h>
#include "Config.h"
#include "App_Protocol_BF_UART.h"
#include "App_MainTask.h"
#include "App_Parameter.h"
/******************************************************************************
* 本文件宏定义 ('#define')
******************************************************************************/
#define FRAME_LEN_MAX		    50		/*最大帧长度*/
#define RECEIVE_STATE           0
#define SEND_STATE              1
/******************************************************************************
* 本文件类型定义 ('typedef')
******************************************************************************/

/******************************************************************************
* 本文件静态函数声明 ('static')
******************************************************************************/
static void vPROTOCOL_Factory_Command(void);
static uint8_t ucPROTOCOL_Send_Data(void);
/******************************************************************************
* 全局变量定义
******************************************************************************/
uint8_t gucRx_Buffer[FRAME_LEN_MAX] = {0};
uint8_t gucTx_Buffer[7] = {0};
uint8_t 					gucRx_Frame_OK 	    = 0;
uint16_t 					gusRx_Len 		    = 0;
stRECEIVED_DATA			    gtReceived_Data 	= {0};
stSEND_DATA				    gtSend_Data			= {0};

uint8_t					    gucTimeOutError 	= 0;
uint8_t                     gucState            = 0;/*状态变量，用于标记当前处于发送状态还是接收状态*/
uint32_t                    gulCommunicationCnt = 0;
uint8_t gucArrErrorCode_BF[] = {0x04,0x05,0x07,0x08,0x09,0x10,0x11,0x12,0x13,0x14,0x15,0x21,0x23,0x24,0x25,0x26};
/*****************************************************************************
* 函数实现 - global ('extern') and local ('static')
******************************************************************************/


/******************************************************************************
** 功  能：获取一帧协议数据
** 参  数：pData-指向数据的指针，Len-数据长度
** 返回值：无
** 备  注：该函数是协议处理模块对外获取数据的接口，接收到完整一帧后，调用该函数进行处理
******************************************************************************/
void vPROTOCOL_Get_OneFrame(uint8_t *pData,uint16_t Len)
{
    memcpy(gucRx_Buffer,pData,Len);		/*将数据拷贝到缓冲区*/
    gusRx_Len = Len;					/*数据长度*/
    gucRx_Frame_OK = 1;					/*接收到一帧标志置1*/
}

/******************************************************************************
** 功  能：解析协议数据
** 参  数：无
** 返回值：无
** 备  注：该函数需要循环调用。用于解析接收到的协议数据和定时发送数据
******************************************************************************/
void vPROTOCOL_Loop(void)
{

    static uint8_t lucLastCmd;
    static uint8_t lucNoAnswerCnt = 0;

    if(gucRx_Frame_OK == 1)
    {
        gulCommunicationCnt = ulBsp_Getticks();
        gucTimeOutError = 0;
        if(gucRx_Buffer[0] == 0x3A && gucRx_Buffer[1] == 0x1C)
        {
            /*内部控制指令*/
            vPROTOCOL_Factory_Command();
            gucState = RECEIVE_STATE;
            lucNoAnswerCnt = 0;
        }
        else
        {
            /*调用八方的协议进行解析*/
            if(gucState == RECEIVE_STATE)
            {
                switch(lucLastCmd)
                {
                case READ_SOC:
                {
                    lucNoAnswerCnt = 0;
                    if(gucRx_Buffer[0] == gucRx_Buffer[1])
                    {
                        gtReceived_Data.Soc = gucRx_Buffer[0];
                        gucState = SEND_STATE;/*转发送状态，发送下一个命令*/
                    }
                    break;
                }
                case READ_CURRENT:
                {
                    lucNoAnswerCnt = 0;
                    if(gucRx_Buffer[0] == gucRx_Buffer[1])
                    {
                        gtReceived_Data.Current = gucRx_Buffer[0];
                        gucState = SEND_STATE;/*转发送状态，发送下一个命令*/
                    }
                    break;
                }
                case READ_SPEED:
                {
                    lucNoAnswerCnt = 0;
                    if(gucRx_Buffer[2] == (uint8_t)(0x20 + gucRx_Buffer[0] + gucRx_Buffer[1]))
                    {
                        gtReceived_Data.Rpm = ((uint16_t)gucRx_Buffer[0] << 8) | gucRx_Buffer[1];
                        gucState = SEND_STATE;/*转发送状态，发送下一个命令*/
                    }
                    break;
                }
                case READ_ERROR:
                {
                    lucNoAnswerCnt = 0;
                    gtReceived_Data.ErrCode = gucRx_Buffer[0];
                    gucState = SEND_STATE;/*转发送状态，发送下一个命令*/
                    break;
                }
                case 0x00:
                {
                    break;
                }
                }
            }
        }
        gucRx_Frame_OK = 0;
    }
    else
    {
        /*通讯超时时间，10s*/
        if(ulBsp_Getticks() - gulCommunicationCnt > 10000)
        {
            gucTimeOutError = 1;
        }

        if(++lucNoAnswerCnt > 2)
        {
            lucNoAnswerCnt = 0;
            gucState = SEND_STATE;
        }
    }

    /*发送状态*/
    if(gucState == SEND_STATE)
    {
        lucLastCmd = ucPROTOCOL_Send_Data();
        gucState = RECEIVE_STATE;
    }
}

/******************************************************************************
** 功  能：设置大灯状态
** 参  数：State-大灯状态。0-关闭，1-打开
** 返回值：无
** 备  注：所有协议通用
******************************************************************************/
void vPROTOCOL_Set_Lamp(uint8_t State)
{
    gtSend_Data.Lamp = State == 0 ? 0 : 1;
}

/******************************************************************************
** 功  能：设置档位范围
** 参  数：Pas_Range-档位值范围，3，4，5，9
** 返回值：无
** 备  注：本函数不对档位值的合理性做检查。请保证传入的档位值正确。
******************************************************************************/
void vPROTOCOL_Set_Pas_Range(int8_t Pas_Range)
{
    gtSend_Data.Pas_Range = Pas_Range;
}

/******************************************************************************
** 功  能：设置档位
** 参  数：Pas-档位值，最大为9，最小为-1,-1表示助推档位
** 返回值：无
** 备  注：本函数不对档位值的合理性做检查。请保证传入的档位值正确。

******************************************************************************/
void vPROTOCOL_Set_Pas(int8_t Pas)
{
    gtSend_Data.Pas = Pas;
}

/******************************************************************************
** 功  能：设置限速值
** 参  数：Speed-限速值
** 返回值：无
** 备  注：本函数不对传入值的合理性做检查。请保证传入的值正确。
******************************************************************************/
void vPROTOCOL_Set_Speed_Limit(uint16_t Speed)
{
    gtSend_Data.Speed_Limit = Speed;
}

/******************************************************************************
** 功  能：设置轮径
** 参  数：Wheel-轮径值（英寸）
** 返回值：无
** 备  注：本函数不对传入值的合理性做检查。请保证传入的值正确。
**         所有协议通用
******************************************************************************/
void vPROTOCOL_Set_Wheel(uint16_t Wheel)
{
    gtSend_Data.Wheel_Diameter = Wheel;
}






/******************************************************************************
** 功  能：设置发送配置数据
** 参  数：无
** 返回值：无
** 备  注：本函数运行后，即开始发送配置数据。直到再次设置发送运行数据或停止
**         所有协议通用
******************************************************************************/
void vPROTOCOL_Send_Config_Data(void)
{

}

/******************************************************************************
** 功  能：设置发送一次配置数据
** 参  数：无
** 返回值：无
** 备  注：本函数运行后，只发送一次配置数据。然后会转为发送运行数据
**         所有协议通用
******************************************************************************/
void vPROTOCOL_Send_Config_Data_OneTime(void)
{

}


/******************************************************************************
** 功  能：设置发送运行数据
** 参  数：无
** 返回值：无
** 备  注：八方协议无用。
******************************************************************************/
void vPROTOCOL_Send_Run_Data(void)
{

}

/******************************************************************************
** 功  能：停止发送数据
** 参  数：无
** 返回值：无
** 备  注：本函数运行后，即停止发送数据。直到再次设置发送配置数据或发送运行数据
**         所有协议通用
******************************************************************************/
void vPROTOCOL_Send_Stop(void)
{

}

/******************************************************************************
** 功  能：获取电池容量
** 参  数：无
** 返回值：-1表示未获取到电池容量，电池容量，0-100%
** 备  注：
******************************************************************************/
int8_t cPROTOCOL_Get_Soc(void)
{
    return gtReceived_Data.Soc;
}

/******************************************************************************
** 功  能：获取当前电流值
** 参  数：无
** 返回值：实际电流值的10倍。比如实际电流12.5A，返回125
******************************************************************************/
uint16_t usPROTOCOL_Get_Current(void)
{
    if(gucTimeOutError == 1)
    {
        return 0;
    }
    else
    {
        return gtReceived_Data.Current * 5;
    }
}



/******************************************************************************
** 功  能：获取当前车速
** 参  数：无
** 返回值：实际车速*10.比如返回120，实际车速即12.0Km/h
******************************************************************************/
uint16_t usPROTOCOL_Get_Speed(void)
{
    if(gucTimeOutError == 0 && gtSend_Data.Wheel_Diameter > 0)
    {
        return 4785U * gtSend_Data.Wheel_Diameter * gtReceived_Data.Rpm / 1000000;
    }
    else
    {
        return 0;
    }
}

/******************************************************************************
** 功  能：获取错误代码
** 参  数：无
** 返回值：十六进制的错误代码
** 备  注：包括30错误代码（通信超时）
**         所有协议通用
******************************************************************************/
uint8_t ucPROTOCOL_Get_Errcode(void)
{
    uint8_t i = 0;
    if(gucTimeOutError == 1)
    {
        return 0x30;
    }
    else
    {
        for(i = 0; i < sizeof(gucArrErrorCode_BF); i++) /*为了屏蔽非协议内错误，修改此处=*/
        {
            if(gtReceived_Data.ErrCode == gucArrErrorCode_BF[i])
            {
                return gtReceived_Data.ErrCode;
            }
        }
        return 0;
    }
}

/******************************************************************************
** 功  能：工厂指令的应答
** 参  数：Cmd-要应答的指令，Response-要应答的参数
** 返回值：无
** 备  注：
******************************************************************************/
static void vPROTOCOL_Factory_Response(uint8_t Cmd,uint16_t Response)
{
    uint8_t lucRxBuffer[8];

    /*这个函数会在中断中调用。按说不应该采用这种阻塞延时的方式。但是因为校准指令的发送实际不确定，不加延时，*/
    /*有可能造成应答帧和前后发送数据的正常帧连在一起，造成工装无法识别。因此强制增加了20ms的延时。*/
    /*这个函数只是在工厂校准用。正常使用不会调用。因此阻塞延时对用户使用性能无影响*/

    vBsp_DelayMs(40);
    lucRxBuffer[0] = 0x3A;  /*0x3A,0x50是应答帧的帧头*/
    lucRxBuffer[1] = 0x50;
    lucRxBuffer[2] = Cmd;
    lucRxBuffer[3] = Response & 0x00FF;
    lucRxBuffer[4] = (Response >> 8) & 0x00FF;
    lucRxBuffer[5] = lucRxBuffer[2] + lucRxBuffer[3] + lucRxBuffer[4]; /*8位的和校验*/
    lucRxBuffer[6] = 0x0D;
    lucRxBuffer[7] = 0x0A;  /*0x0D,0x0A是固定帧尾*/

    USART_MCU_Send_Frame(lucRxBuffer,8);
    vBsp_DelayMs(40);
}

/******************************************************************************
** 功  能：工厂指令的解析
** 参  数：无
** 返回值：无
** 备  注：
******************************************************************************/
void vPROTOCOL_Factory_Command(void)
{
    uint16_t n;
    uint16_t lusCheckSum = 0;
    uint16_t lusResponse;

    for(n = 0; n < gusRx_Len - 5; n++)
    {
        lusCheckSum += gucRx_Buffer[1 + n];		/*计算校验值，和校验*/
    }
    if((gucRx_Buffer[3] == gusRx_Len - 8) && (lusCheckSum == gucRx_Buffer[gucRx_Buffer[3] + 5] * 256UL + gucRx_Buffer[gucRx_Buffer[3] + 4]))
    {
        switch(gucRx_Buffer[2])
        {
        case 0x5D:		/*电压校准*/
        {
            lusResponse = vVoltage_Calibration();
            vPROTOCOL_Factory_Response(0x5D,lusResponse);
            break;
        }
        case 0x8E:		/*清除ODO*/
        {
            vClear_ODO();
            vPROTOCOL_Factory_Response(0x8E,5678); /*5678是针对清ODO的固定响应*/
            break;
        }
        case 0x6E:		/*校准光感*/
        {
            lusResponse = vPhotoDiode_Calibration();
            vPROTOCOL_Factory_Response(0x6E,lusResponse);
            break;
        }
        case 0x71:		/*自动校准时间*/
        {
            vDateTime_Calibration(gucRx_Buffer[7],gucRx_Buffer[8],gucRx_Buffer[9],gucRx_Buffer[4],gucRx_Buffer[5],gucRx_Buffer[6]);
            vPROTOCOL_Factory_Response(0x71,1234); /*1234是针对校准时间的固定响应*/
            break;
        }
        default:
            break;
        }
    }
}




/******************************************************************************
** 功  能：设置发送运行数据
** 参  数：无
** 返回值：刚刚发送的读请求指令，如果是写数据，则返回0
** 备  注：
******************************************************************************/
static uint8_t ucPROTOCOL_Send_Data(void)
{
    static uint8_t lucIndex = 3;
    static uint8_t lucSubIndex = 0;
    const static uint8_t Pas_3[] = {6,0,12,2,3};
    const static uint8_t Pas_4[] = {6,0,1,12,21,3};
    const static uint8_t Pas_5[] = {6,0,11,13,21,23,3};
    const static uint8_t Pas_9[] = {6,0,1,11,12,13,2,21,22,23,3};
    uint16_t lusRpm;
    uint8_t lucCmd = 0;
    switch(lucIndex)
    {
    case 0:
    {
        gucTx_Buffer[0] = 0x11;             /*读指令的起始字节*/
        gucTx_Buffer[1] = READ_SPEED;       /*读取车速*/
        USART_MCU_Send_Frame(gucTx_Buffer,2);
        lucCmd = READ_SPEED;
        lucIndex = 1;
        break;
    }
    case 1:
    {
        gucTx_Buffer[0] = 0x11;             /*读指令的起始字节*/
        gucTx_Buffer[1] = READ_CURRENT;     /*读取电流*/
        USART_MCU_Send_Frame(gucTx_Buffer,2);
        lucCmd = READ_CURRENT;
        lucIndex = 2;
        break;
    }
    case 2:
    {
        gucTx_Buffer[0] = 0x16;             /*写指令的起始字节*/
        gucTx_Buffer[1] = WRITE_PAS;        /*写档位*/
        if(gtSend_Data.Pas_Range == 3)
        {
            gucTx_Buffer[2] = Pas_3[gtSend_Data.Pas + 1];
        }
        else if(gtSend_Data.Pas_Range == 4)
        {
            gucTx_Buffer[2] = Pas_4[gtSend_Data.Pas + 1];
        }
        else if(gtSend_Data.Pas_Range == 5)
        {
            gucTx_Buffer[2] = Pas_5[gtSend_Data.Pas + 1];
        }
        else if(gtSend_Data.Pas_Range == 9)
        {
            gucTx_Buffer[2] = Pas_9[gtSend_Data.Pas + 1];
        }
        else
        {
            gucTx_Buffer[2] = 0;
        }
        gucTx_Buffer[3] = gucTx_Buffer[0] + gucTx_Buffer[1] + gucTx_Buffer[2];
        USART_MCU_Send_Frame(gucTx_Buffer,4);
        lucCmd = 0x00;/*写指令返回0x00*/
        lucIndex = 3;
        break;
    }
    case 3:
    {
        switch(lucSubIndex)
        {
        case 0:/*读电量*/
        {
            gucTx_Buffer[0] = 0x11;
            gucTx_Buffer[1] = READ_SOC;/*读电量*/
            USART_MCU_Send_Frame(gucTx_Buffer,2);
            lucCmd = READ_SOC;
            lucSubIndex = 1;
            break;
        }
        case 1:/*读错误代码*/
        {
            gucTx_Buffer[0] = 0x11;
            gucTx_Buffer[1] = READ_ERROR;/*读错误代码*/
            USART_MCU_Send_Frame(gucTx_Buffer,2);
            lucCmd = READ_ERROR;
            lucSubIndex = 2;
            break;
        }
        case 2:/*写限速*/
        {
            gucTx_Buffer[0] = 0x16;
            gucTx_Buffer[1] = WRITE_SPEEDLIMIT;/*写限速*/
            lusRpm = gtSend_Data.Speed_Limit * 10000000U / (4785 * gtSend_Data.Wheel_Diameter);
            gucTx_Buffer[2] = lusRpm >> 8;
            gucTx_Buffer[3] = lusRpm & 0x00FF;
            gucTx_Buffer[4] = gucTx_Buffer[0] + gucTx_Buffer[1] + gucTx_Buffer[2] + gucTx_Buffer[3];
            USART_MCU_Send_Frame(gucTx_Buffer,5);
            lucCmd = 0x00;
            lucSubIndex = 3;
            break;
        }
        case 3:/*写大灯状态*/
        {
            gucTx_Buffer[0] = 0x16;
            gucTx_Buffer[1] = WRITE_LAMP;/*写大灯状态*/
            gucTx_Buffer[2] = 0xF0 | gtSend_Data.Lamp;
            USART_MCU_Send_Frame(gucTx_Buffer,3);
            lucCmd = 0x00;
            lucSubIndex = 0;
            break;
        }
        }
        lucIndex = 0;
        break;
    }
    }
    return lucCmd;
}

/******************************************************************************
** 功  能：开机上电时，设置默认要发送给控制器的数据
** 参  数：无
** 返回值：无
** 备  注：
******************************************************************************/
void vPROTOCOL_Set_Value_When_PowerOn(void)
{
    gtReceived_Data.Soc = -1;/*SOC标志置为-1，表示没有接收到SOC值*/

    vPROTOCOL_Set_Wheel(usGet_Wheel_Size(gtParam.Wheel_Diameter));
    vPROTOCOL_Set_Pas_Range(gtParam.Pas_Max);
    vPROTOCOL_Set_Pas(0);/*开机LOGO界面发0档*/
    vPROTOCOL_Set_Speed_Limit(gtParam.Speed_Limit);
    vPROTOCOL_Send_Run_Data();
}

/******************************************************************************
** 功  能：复位通信超时计数变量
** 参  数：无
** 返回值：无
** 备  注：
******************************************************************************/
void vPROTOCOL_ResetCommunicationOverTime(void)
{
    __disable_irq();
    gucTimeOutError = 0;
    gulCommunicationCnt = ulBsp_Getticks();
    __enable_irq();

}

/******************************************************************************
** 功  能：退出设置时，设置默认要发送给控制器的数据
** 参  数：无
** 返回值：无
** 备  注：
******************************************************************************/
void vPROTOCOL_Set_Value_When_ExitSetting(void)
{
    vPROTOCOL_Set_Wheel(usGet_Wheel_Size(gtParam.Wheel_Diameter));
    vPROTOCOL_Set_Speed_Limit(gtParam.Speed_Limit);
}

